
package com.chazaqdev.meatbagsorter

import com.sun.imageio.plugins.jpeg.JPEGMetadata;
import com.sun.media.jai.codec.SeekableStream
import java.awt.RenderingHints
import java.awt.image.renderable.ParameterBlock

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter
import javax.imageio.stream.MemoryCacheImageOutputStream;
import javax.media.jai.OpImage
import javax.media.jai.PlanarImage;
import javax.media.jai.RenderedOp
import javax.media.jai.JAI

import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.formats.jpeg.JpegImageMetadata;
import org.apache.sanselan.formats.tiff.TiffImageMetadata;
import org.apache.sanselan.formats.tiff.constants.ExifTagConstants;
import org.apache.sanselan.formats.tiff.constants.TiffConstants;
import org.apache.sanselan.formats.tiff.constants.TiffFieldTypeConstants;
import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
import org.apache.sanselan.formats.tiff.write.TiffOutputField;
import org.apache.sanselan.formats.tiff.write.TiffOutputSet;

import java.awt.image.renderable.ParameterBlock;
import java.nio.ByteBuffer;

import org.apache.sanselan.formats.tiff.constants.ExifTagConstants;



class ResizeImage {
	/**
	 * The JAI.create action name for handling a stream.
	 */
	private static final String JAI_STREAM_ACTION = "stream";
	
	/**
	* The JAI.create action name for handling a stream.
	*/
   private static final String JAI_ROTATE_ACTION = "rotate";

	/**
	 * The JAI.create action name for handling a resizing using a subsample averaging technique.
	 */
	private static final String JAI_SUBSAMPLE_AVERAGE_ACTION = "SubsampleAverage";

	/**
	 * The JAI.create encoding format name for JPEG.
	 */
	private static final String JAI_ENCODE_FORMAT_JPEG = "JPEG";

	/**
	 * The JAI.create action name for encoding image data.
	 */
	private static final String JAI_ENCODE_ACTION = "encode";

	/**
	 * The http content type/mime-type for JPEG images.
	 */
	private static final String JPEG_CONTENT_TYPE = "image/jpeg";

	private int mMaxWidth = 800;

	private int mMaxWidthThumbnail = 150;

	
	public byte[] rotateCorrectly(byte[] pImageData){
		byte[] newImageData = pImageData
		
		//First find out whether we should rotate the thing
		IImageMetadata metadata = null
		JpegImageMetadata jpeg = null
		TiffImageMetadata exif = null
		TiffOutputSet outputSet = null
		try {
			metadata = Sanselan.getMetadata (pImageData)
		} catch (Exception e) {
		}
		
		if (metadata != null) {
			exif = (TiffImageMetadata)metadata
//			jpeg = (JPEGMetadata)metadata
//			if (jpeg != null) {
//				exif = jpeg.getExif()
				if (exif != null) {
					outputSet = exif.getOutputSet()
					if (outputSet != null) {
						TiffOutputField tof = outputSet.findField(TiffConstants.EXIF_TAG_ORIENTATION)
						int rotate = (tof == null ? 1 : tof.getIntValue())
						if (rotate != 1) {
							
							float angle = (
									(	rotate == ExifTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_270_CW
									||	rotate == ExifTagConstants.ORIENTATION_VALUE_ROTATE_90_CW
									)
										? (float)Math.toRadians(90)
										: (float)Math.toRadians(270)
								)
							println("Going to rotate image " + angle + " radians")
							
							outputSet.removeField(TiffConstants.EXIF_TAG_ORIENTATION)
							
							ParameterBlock paramBlock = new ParameterBlock();
							paramBlock.addSource (pImageData)
							paramBlock.add((float)0)
							paramBlock.add((float)0)
							paramBlock.add(angle)
							paramBlock.add(new javax.media.jai.InterpolationBicubic(10))
							RenderingHints qualityHints = new RenderingHints (RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)
							def encoderOutputStream = new ByteArrayOutputStream()
							RenderedOp renderOp = JAI.create(JAI_ROTATE_ACTION, paramBlock, qualityHints)

							try {
								Iterator<ImageWriter> itr = ImageIO.getImageWritersByFormatName("JPEG")
								if (itr.hasNext()) {
									ImageWriter imw = itr.next()
									ImageWriteParam iwp = imw.getDefaultWriteParam()
									try {
										iwp.setCompressionMode ImageWriteParam.MODE_EXPLICIT
										iwp.setCompressionQuality 0.99
										
									} catch (Exception e) {
										e.printStackTrace()
									}
									imw.setOutput (new MemoryCacheImageOutputStream(encoderOutputStream))
									imw.write( null, new IIOImage( renderOp.getAsBufferedImage(), null, null ), iwp )
									imw.dispose()
								}
								
								newImageData = encoderOutputStream.toByteArray()
							} catch (Exception e) {
								e.printStackTrace()
							}
							
						}
					}
				}
//			}
		}
		
		try {
			metadata = Sanselan.getMetadata (newImageData)	
		} catch (Exception e) {
		}
		
		if (metadata != null) {
			jpeg = (JPEGMetadata)metadata
			if (jpeg != null) {
				exif = jpeg.getExif()
				if (exif != null) {
					outputSet = exif.getOutputSet()
					if (outputSet != null) {
						ByteBuffer bf = ByteBuffer.allocate (4)
						bf.putInt (1)
						
						TiffOutputField rotateField = new TiffOutputField (
							ExifTagConstants.EXIF_TAG_ORIENTATION, 
							TiffFieldTypeConstants.FIELD_TYPE_LONG, 
							4, 
							bf.array()
							)
						TiffOutputDirectory tod = outputSet.getOrCreateExifDirectory()
						tod.add (rotateField)
					}
				}
			}
			
		}
		
		
		
		
		return newImageData
	}
	
	public byte[] resizeImageAsJPG(byte[] pImageData, int pMaxWidth) throws IOException {
		InputStream imageInputStream = new ByteArrayInputStream(pImageData);
		// read in the original image from an input stream
		SeekableStream seekableImageStream = SeekableStream.wrapInputStream(imageInputStream, true);
		RenderedOp originalImage = JAI.create(JAI_STREAM_ACTION, seekableImageStream);
		((OpImage) originalImage.getRendering()).setTileCache(null);
		int origImageWidth = originalImage.getWidth();
		// now resize the image
		double scale = 1.0;
		if (pMaxWidth > 0 && origImageWidth > pMaxWidth) {
			scale = (double) pMaxWidth / originalImage.getWidth();
		}
		ParameterBlock paramBlock = new ParameterBlock();
		paramBlock.addSource(originalImage); // The source image
		paramBlock.add(scale); // The xScale
		paramBlock.add(scale); // The yScale
		paramBlock.add(0.0); // The x translation
		paramBlock.add(0.0); // The y translation

		RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);

		RenderedOp resizedImage = JAI.create(JAI_SUBSAMPLE_AVERAGE_ACTION, paramBlock, qualityHints);
		def encoderOutputStream = new ByteArrayOutputStream()
		try {
			Iterator<ImageWriter> itr = ImageIO.getImageWritersByFormatName("JPEG")
			if (itr.hasNext()) {
				ImageWriter imw = itr.next()
				ImageWriteParam iwp = imw.getDefaultWriteParam()
				try {
					iwp.setCompressionMode ImageWriteParam.MODE_EXPLICIT
					iwp.setCompressionQuality 0.99
					
				} catch (Exception e) {
					e.printStackTrace()
				}
				imw.setOutput (new MemoryCacheImageOutputStream(encoderOutputStream))
				imw.write( null, new IIOImage( resizedImage.getAsBufferedImage(), null, null ), iwp )
				imw.dispose()
			}
		} catch (Exception e) {
			e.printStackTrace()
		}
		
		// lastly, write the newly-resized image to an output stream, in a specific encoding
//		ByteArrayOutputStream encoderOutputStream = new ByteArrayOutputStream();
//		JAI.create(JAI_ENCODE_ACTION, resizedImage, encoderOutputStream, JAI_ENCODE_FORMAT_JPEG, null);
		// Export to Byte Array
		byte[] resizedImageByteArray = encoderOutputStream.toByteArray();
		return resizedImageByteArray;
	}
}
